#include <assert.h>
#include "token.h"

const Token::lookup_t Token::keywords[] =
{
   { "nil", T_NIL },
   { "true", T_TRUE },
   { "false", T_FALSE },

   { "eq", T_EQ },
   { "ne", T_NE },
   { "lt", T_LT },
   { "gt", T_GT },
   { "le", T_LE },
   { "ge", T_GE },

   { "not", T_NOT },
   { "and", T_AND },
   { "or", T_OR },
   { "xor", T_XOR },

   { "lsh", T_LSHIFT },
   { "rsh", T_RSHIFT },

   { "if", T_IF },
   { "else", T_ELSE },
   { "return", T_RETURN },

   { "this", T_THIS },
   { "super", T_SUPER },

   { "import", T_IMPORT },
   { "enum", T_ENUM },
   { "class", T_CLASS },
   { "immutable", T_IMMUTABLE },
   { "interface", T_INTERFACE },

   { "includes", T_INCLUDES },
   { "extends", T_EXTENDS },
   { "implements", T_IMPLEMENTS },
   { "where", T_WHERE },

   { "literal", T_LITERAL },

   { "protected", T_PROTECTED },
   { "package", T_PACKAGE },
   { "public", T_PUBLIC },
   { "new", T_NEW },
   { "throws", T_THROWS },

   { "var", T_VAR },

   { "", T_NIL }
};

const Token::lookup_t Token::symbols[] =
{
   { "{", T_LBRACE },
   { "}", T_RBRACE },
   { "(", T_LPAREN },
   { ")", T_RPAREN },
   { "[", T_LBRACKET },
   { "]", T_RBRACKET },
   { "<", T_LANGLE },
   { ">", T_RANGLE },
   { ",", T_COMMA },
   { ".", T_DOT },
   { ":", T_TYPE_SEPARATOR },
   { ";", T_TERMINATOR },

   { "=", T_ASSIGN },
   { "+", T_PLUS },
   { "-", T_MINUS },
   { "*", T_STAR },
   { "/", T_DIVIDE },
   { "%", T_MOD },
   { "^", T_POWER },

   { "**", T_WEAK },
   { "->", T_RESULTS },

   { "", T_NIL }
};

const std::string Token::none = "<invalid token>";

Token::Token( const Location& loc, type_t type ) :
   Location( loc )
{
   _type = type;
}

const std::string& Token::text( type_t type )
{
   const lookup_t* p;

   p = symbols;

   while( !p->data.empty() )
   {
      if( p->type == type )
      {
         return p->data;
      }

      p++;
   }

   p = keywords;

   while( !p->data.empty() )
   {
      if( p->type == type )
      {
         return p->data;
      }

      p++;
   }

   return none;
}

bool Token::is_symbol( const std::string& data )
{
   // accept any single character symbol, for error reporting
   if( data.length() <= 1 )
   {
      return true;
   }

   const lookup_t* p = symbols;

   while( !p->data.empty() )
   {
      if( p->data == data )
      {
         return true;
      }

      p++;
   }

   return false;
}

const std::string& Token::string() const
{
   if( !_data.empty() )
   {
      return _data;
   }

   return text( _type );
}

void Token::mutate( type_t type )
{
   _type = type;
}

void Token::finalize( const std::string& data )
{
   if( (_type == T_IDENTIFIER) && (data.length() > 0) )
   {
      if( (data[0] >= 'A') && (data[0] <= 'Z') )
      {
         // identifier that starts with upper case is a type
         _type = T_TYPE;

         // a type can't contain an underscore
         for( size_t i = 0; i < data.length(); i++ )
         {
            if( data[i] == '_' )
            {
               _type = T_UNKNOWN_SYMBOL;
               break;
            }
         }
      } else {
         // a leading underscore indicates a member
         if( data[0] == '_' )
         {
            _type = T_MEMBER;
         }

         // a non-type identifier can't contain upper case
         for( size_t i = 0; i < data.length(); i++ )
         {
            if( (data[i] >= 'A') && (data[i] <= 'Z') )
            {
               _type = T_UNKNOWN_SYMBOL;
               break;
            }
         }
      }
   }

   const lookup_t* p;

   switch( _type )
   {
   case T_IDENTIFIER:
      p = keywords;
      break;

   case T_UNKNOWN_SYMBOL:
      p = symbols;
      break;

   default:
      p = NULL;
   }

   if( p )
   {
      while( !p->data.empty() )
      {
         if( p->data == data )
         {
            _type = p->type;
            break;
         }

         p++;
      }
   }

   if(
      (_type == T_UNKNOWN_SYMBOL) ||
      (_type == T_UNEXPECTED_CHAR) ||
      (_type == T_BINARY) ||
      (_type == T_HEXADECIMAL) ||
      (_type == T_INTEGER) ||
      (_type == T_REAL) ||
      (_type == T_IMAGINARY) ||
      (_type == T_STRING) ||
      (_type == T_IDENTIFIER) ||
      (_type == T_MEMBER) ||
      (_type == T_TYPE)
      )
   {
      _data = data;
   }
}

